// Bus.cpp
//
// A round-robin-scheduled bus.
//
//
#include <fcntl.h>
#include <string.h>

#include "base/DataRequest.h"
#include "base/Entity.h"
#include "base/LoadRequest.h"
#include "base/Packet.h"
#include "base/RawFlushRequest.h"
#include "base/Response.h"
#include "base/Simulator.h"
#include "base/SnoopACK.h"
#include "base/SnoopRequest.h"
#include "base/SnoopResponse.h"
#include "base/StoreRequest.h"
#include "mem/Bus.h"
#include "mem/LineFlags.h"
#include "mem/SplitBus.h"
#include "mem/CancelRequestsRequest.h"

//#define DEBUG
NEW_DELETE_OPERATORS_CPP(BufferSlot,10);
NEW_DELETE_OPERATORS_CPP(Bus,100);
NEW_DELETE_OPERATORS_TEMPLATE_CPP(__quote__(Map<int, PriorityQueue<uint, std::greater<uint> >*>), 1);
NEW_DELETE_OPERATORS_TEMPLATE_CPP(List<BufferSlot*>, 100);
           
class bf_slot_less : public std::binary_function <BufferSlot* ,BufferSlot* ,bool>
{
 public:
    bool operator () (BufferSlot* a, BufferSlot* b)
    {
        return a->getArbTime() < b->getArbTime();
    }
};


Bus::Bus (char* _name,
          SplitBus* _sBus,
          int _width,
          int _arbDelay,
          int _bufferSize,
          StatisticsCollection* _statsInput,
          StatisticsCollection* _statsOutput,
          StatisticsCollection* _statsQueue,
          Logger* log)
:
    Entity(_name, CLOCK_WITH_INTERCONNECT),
    statsInput(_statsInput),
    statsOutput(_statsOutput),
    statsQueue(_statsQueue),
    width(_width),
    arbDelay(_arbDelay),
    bufferSize(_bufferSize),
    packetCount(0)
{
    sBus = _sBus;
    busLog = log;
    
    arbRobin = packets.begin();

    registerPacketHandler(typeid(SnoopResponse),
                          (PacketHandlerPtr)&Bus::handle_incoming);
    registerPacketHandler(typeid(Response),
                          (PacketHandlerPtr)&Bus::handle_incoming);
    registerPacketHandler(typeid(LoadRequest),
                          (PacketHandlerPtr)&Bus::handle_incoming);
    registerPacketHandler(typeid(StoreRequest),
                          (PacketHandlerPtr)&Bus::handle_incoming);
    registerPacketHandler(typeid(SnoopACK),
                          (PacketHandlerPtr)&Bus::handle_snoopACK);
    registerPacketHandler(typeid(RawFlushRequest),
                          (PacketHandlerPtr)&Bus::handle_RawFlushRequest);                          
    ignorePacketType(typeid(CancelRequestsRequest));

    transferTime = 0;   
    gradualDelivery =false;
    arbitratedPacket = NULL;
    packetBuffer = NEW List<BufferSlot*>;

    bktInputCyclesIdle        = statsInput->addBucket("Input-Cycles-Idle");       
    bktInputCyclesArbitration = statsInput->addBucket("Input-Cycles-Arbitration");
    bktInputCyclesFirst       = statsInput->addBucket("Input-Cycles-First");
    bktInputCyclesFollowing   = statsInput->addBucket("Input-Cycles-Following");

    bktOutputCyclesIdle      = statsOutput->addBucket("Output-Cycles-Idle");
    bktOutputCyclesWaiting   = statsOutput->addBucket("Output-Cycles-Waiting");
    bktOutputCyclesDestBusy  = statsOutput->addBucket("Output-Cycles-Dest-Busy");
    bktOutputCyclesFirst     = statsOutput->addBucket("Output-Cycles-First");

    histQueueOccupancy         = statsQueue->addHistogram("QueueOccupancy", bufferSize);
    histEntitiesWaitingForDest = statsQueue->addHistogram("EntitiesWaitingForDest", bufferSize);
}

Bus::~Bus () {
    List<BufferSlot*>::iterator slotIdx;
    for (slotIdx  = packetBuffer->begin();
         slotIdx != packetBuffer->end();
         slotIdx++)
    {
        BufferSlot* slot = *slotIdx; 
        ASSERT(slot->getPacket() != NULL);
        Response* resp = dynamic_cast<Response*>(slot->getPacket());
        if (resp != NULL) {
            DataRequest* dr = dynamic_cast<DataRequest*>(resp->getRequest());
            if (dr != NULL) {
                dr->recursiveDelete();
            }        
            delete resp->getRequest();
        }
        delete slot->getPacket();
        delete slot;
    }            

    delete packetBuffer;

    if (arbitratedPacket != NULL) {
        Response* resp = dynamic_cast<Response*>(arbitratedPacket);
        if (resp != NULL) {
            DataRequest* dr = dynamic_cast<DataRequest*>(resp->getRequest());
            if (dr != NULL) {
                dr->recursiveDelete();
            }        
            delete resp->getRequest();
        }
        delete arbitratedPacket;
    }
    
    for (Map<Entity*, List<Packet*>* >::iterator i = packets.begin();
         i != packets.end();
         i++)
    {
        List<Packet*>* list = i->second;
        List<Packet*>::iterator pks;
        for (pks = list->begin(); pks != list->end(); pks++) {
            Response* resp = dynamic_cast<Response*>(*pks);
            if (resp != NULL) {
                DataRequest* dr =
                    dynamic_cast<DataRequest*>(resp->getRequest());
                if (dr != NULL) {
                    dr->recursiveDelete();
                }        
                delete resp->getRequest();
            }
            delete *pks;
        }
        delete list;
    }
}

bool Bus::countVoteHelper (Vector<Bucket*>* buckets) {
    bool voted = false;
    Vector<Bucket*>::reverse_iterator bktIdx;
    for (bktIdx = buckets->rbegin();
         bktIdx != buckets->rend();
         bktIdx++)
    {
        Bucket* bkt = *bktIdx;
        if (!voted && bkt->getVoteStatus()) {
            bkt->elect();
            voted = true;
        }
        bkt->unvote();
    }
    return voted;
}

void Bus::countVotes () {
    Vector<Bucket*>* buckets = getInputStatistics()->getBuckets();

    if (!countVoteHelper(buckets)) {
        // austenmc: this should die---if you change this to a debug message just
        // b/c you have unaccounted cycles, Christos will find you, and kill you.
        busLog->e("%s:%lli: unaccounted input cycle\n",
                  getName(),
                  global_sim->getSimclock());
    }

    buckets = getOutputStatistics()->getBuckets();

    if (!countVoteHelper(buckets)) {
        // austenmc: this should die---if you change this to a debug message just
        // b/c you have unaccounted cycles, Christos will find you, and kill you.
        busLog->e("%s:%lli: unaccounted output cycle\n",
                  getName(),
                  global_sim->getSimclock());
    }
}

Result Bus::handle_incoming (Packet* r) {
    if (this == sBus->getResponseBus()
        && r->getDestination() == sBus->getRoutingEntity()
        && !IS_A(SnoopResponse*, r))
    {
        // check if the response's destination is mine,
        Response* rs = CAST_OR_DIE(Response*, r);
        StoreRequest* sr = CAST_OR_DIE(StoreRequest*, rs->getRequest());
        busLog->d("%s:%lli:%0llx %s STORE:DONE\n",
                  name,
                  global_sim->getSimclock(),
                  sr->getAddress(),
                  rs->toString());                    

        sBus->decOutstandingStores();
        
        delete sr;
        delete rs;  
    } else {
        if (packets.find(r->getSource()) == packets.end()) {
            // havent seen this device yet...
            packets[r->getSource()] = NEW List<Packet*>;
            addedDevice = true;
        }

        addPacket(r);

        busLog->d("%s:%lli    IN: %s\n",
                  name,
                  global_sim->getSimclock(),
                  r->toString());
    }
    
    return success;
}

Result Bus::handle_snoopACK (Packet* r) {
    SnoopACK* snoopACK = CAST_OR_DIE(SnoopACK*, r);
    SnoopRequest* snr = CAST_OR_DIE(SnoopRequest*, snoopACK->getRequest());
    Request* origRequest = CAST_OR_DIE(Request*, snr->getPacket());

    // find the slot of the request
    List<BufferSlot*>::iterator slotIdx;
    for (slotIdx  = packetBuffer->begin();
         slotIdx != packetBuffer->end();
         slotIdx++)
    {
        // pick up the packet in the order of increasing arbitration time
        BufferSlot* slot = *slotIdx;           

        // check if the packet is ready to go.
        if (slot->match(origRequest)) {
            busLog->d("%s:%lli ACK: [%s]%s[%d]\n",
                      name,
                      global_sim->getSimclock(),
                      snoopACK->getSource()->getName(),
                      origRequest->toString(),
                      (uint)snoopACK->getFlag());
            // we found the slot.
            slot->recordACK(snoopACK);

            // if the flag is RESPONSE_SNOOP_RESPONSE,
            // the SnoopRequest will be used by SnoopResponse. it should not be
            // deleted here.
            if (snoopACK->getFlag() != RESPONSE_SNOOP_RESPONSE) {
                delete snr;
            }
            delete r;
            return success;
        }            
    }

    // TODO: for now, we do not consider the case where the request is
    // cancelled.
    busLog->e("%s:%lli Received SnoopACK for unknown request %s from %s.\n",
              name,
              global_sim->getSimclock(),
              origRequest->toString(),
              r->getSource()->getName());
    ASSERT(false);
    return failure;    
}

Result Bus::handle_RawFlushRequest (Packet* r) {
    RawFlushRequest* rfr = (RawFlushRequest*)r;

    // check the slot buffers
    flushPacketsInBuffers(rfr);
    
    // check the arbitrated packet
    if (arbitratedPacket != NULL && !gradualDelivery) {
        flushPacketUnderArbitration(rfr);
    }

    // check the arbitrated map
    flushPacketsInForArbitration(rfr);
    
    return success;    
}

void Bus::flushPacketsInBuffers (RawFlushRequest* rfr) {
    List<BufferSlot*>::iterator slotIdx;
    for (slotIdx  = packetBuffer->begin();
         slotIdx != packetBuffer->end();
         slotIdx++)
    {
        // pick up the packet in the order of increasing arbitration time
        BufferSlot* slot = *slotIdx; 
        DataRequest* dr = NULL;
        bool hasDirtyLine = false;
        if (this == sBus->getRequestBus()) {
            dr = dynamic_cast<DataRequest*>(slot->getPacket());
            if (dr != NULL) {
                LoadRequest* lr = dynamic_cast<LoadRequest*>(dr);
                if (lr != NULL) {
                    if (lr->aborted &&
                        slot->getFlag() == RESPONSE_SNOOP_RESPONSE)
                    {
                        hasDirtyLine = true;
                    }
                } else if (dynamic_cast<StoreRequest*>(dr)) {
                    hasDirtyLine = true;
                }  
            }
        } else if (this == sBus->getResponseBus()) {
            SnoopResponse* snr = dynamic_cast<SnoopResponse*>(slot->getPacket());
            if (snr != NULL) {
                SnoopRequest* srq = CAST_OR_DIE(SnoopRequest*, snr->getRequest());
                dr = CAST_OR_DIE(LoadRequest*, srq->getPacket());
                hasDirtyLine = true;  
            }
        } else {
            ASSERT(false);
        }
        
        
        if (hasDirtyLine && 
            !dr->isFlushed() && 
            rfr->inBounds(dr->getAddress(),
                          dr->getDataSize())) 
        {
            memcpy(ADDR_UINT32STAR(dr->getAddress()),
                   dr->getData(), dr->getDataSize()); 
             
            busLog->d("%s:%lli:%0llx: REQUEST BUS : RAW FLUSH FROM SNOOP: [%s]\n",
                      name,
                      global_sim->getSimclock(),
                      dr->getAddress(),
                      dr->getSource()->getName());
            for (uint j = 0; j < dr->getDataSize()/WORD_SIZE; j++) {
                busLog->d("%08x ", ((uint*)dr->getData())[j]);
            }
            busLog->d("\n");

            dr->setFlushed(true);
        }            
    }
}

void Bus::flushPacketUnderArbitration (RawFlushRequest* rfr) {
    DataRequest* dr = NULL;
    bool hasDirtyLine = false;
    if (this == sBus->getRequestBus()) {
        dr = dynamic_cast<StoreRequest*>(arbitratedPacket);
        if (dr != NULL) {
            hasDirtyLine = true;
        }            
    } else if (this == sBus->getResponseBus()) {
        SnoopResponse* snr = dynamic_cast<SnoopResponse*>(arbitratedPacket);
        if (snr != NULL) {         
            SnoopRequest* srq = CAST_OR_DIE(SnoopRequest*, snr->getRequest());
            dr = CAST_OR_DIE(LoadRequest*, srq->getPacket());
            hasDirtyLine = true;            
        }
    } else {
        ASSERT(false);
    }

    if (hasDirtyLine && 
        !dr->isFlushed() && 
        rfr->inBounds(dr->getAddress(),
                      dr->getDataSize())) 
    {
        busLog->d("%s:%lli:%0llx: REQUEST BUS : RAW FLUSH FROM ARBITRATED PACKET: [%s]\n",
                  name,
                  global_sim->getSimclock(),
                  dr->getAddress(),
                  dr->getSource()->getName());
        for (uint j = 0; j < dr->getDataSize()/WORD_SIZE; j++) {
            busLog->d("%08x ", ((uint*)dr->getData())[j]);
        }
        busLog->d("\n");   
        
        memcpy(ADDR_UINT32STAR(dr->getAddress()),
               dr->getData(), dr->getDataSize()); 

        dr->setFlushed(true);
    }        
}

void Bus::flushPacketsInForArbitration (RawFlushRequest* rfr) {
    for (Map<Entity*, List<Packet*>* >::iterator i = packets.begin(); i != packets.end(); i++)
    {
        List<Packet*>* list = i->second;

        List<Packet*>::iterator pks;
        for (pks = list->begin(); pks != list->end(); pks++) {
            DataRequest* dr = NULL;
            bool hasDirtyLine = false;
            if (this == sBus->getRequestBus()) {
                dr = dynamic_cast<DataRequest*>(*pks);
                if (dr != NULL) {       
                    if (dynamic_cast<StoreRequest*>(dr)) {
                        hasDirtyLine = true;
                    }  
                }
            } else if (this == sBus->getResponseBus()) {
                SnoopResponse* snr = dynamic_cast<SnoopResponse*>(*pks);
                if (snr != NULL) {
                    SnoopRequest* srq = CAST_OR_DIE(SnoopRequest*, snr->getRequest());
                    dr = CAST_OR_DIE(LoadRequest*, srq->getPacket());
                    hasDirtyLine = true;  
                }
            } else {
                ASSERT(false);
            }
        

            if (hasDirtyLine && 
                !dr->isFlushed() && 
                rfr->inBounds(dr->getAddress(),
                              dr->getDataSize())) 
            {       
                memcpy(ADDR_UINT32STAR(dr->getAddress()),
                       dr->getData(), dr->getDataSize()); 
                 
                busLog->d("%s:%lli:%0llx: REQUEST BUS : RAW FLUSH FROM PACKETS: [%s]\n",
                          name,
                          global_sim->getSimclock(),
                          dr->getAddress(),
                          dr->getSource()->getName());
                for (uint j = 0; j < dr->getDataSize()/WORD_SIZE; j++) {
                    busLog->d("%08x ", ((uint*)dr->getData())[j]);
                }
                busLog->d("\n");   

                dr->setFlushed(true);
            }
        }
    }
}      


    
// void tick()
//
// Tell the bus to take a cycle.
//
// This is the heart of the bus: it does all the management of requests
// and arbitration. There are four units running concurrently (think of
// this like an HDL): each cycle we have to deal with the current state of
// each of these four units.
//
void Bus::tick () {

    bktInputCyclesIdle->vote();
    bktOutputCyclesIdle->vote();

    // ------------------
    // 1. Packet Transfer
    // ------------------
    // Check if we are in the middle of receiving a packet through wires.
    // 

    if (transferTime) {
        // we've arbitrated a packet and still in receiving the arbitrated
        // packet.
        transferArbitratedPacketToBuffer();
    }

    // ---------------
    // 2. Arbitration
    // ---------------
    // Check all request waiting for arbitration.
    // Start searching at arbRobin, wrapping when you get to getDevCount().
    //
    
    if (arbitratedPacket == NULL) { 
        // arbitrate a packet 
        pickUpPacketToArbitrate();
    }
    
    // ------------------------
    // 3. Buffer Processing
    // ------------------------
    //
    // Check if there is a packet to send to the next hop.
    // while the arbitration is done in order, dispatching packet is done
    // out of order. this is important for accurate snoop operation
    // model.

    histQueueOccupancy->add(packetBuffer->size());
    
    if (!packetBuffer->empty()) {
        bktOutputCyclesWaiting->vote(); 

        // look for the packet that satisfies three conditions
        // 1. it is ready to go. (e.g. all snoop responses are received)
        // 2. the destination is ready to receive it.
        // 3. the oldest among the packets satisfying 1 and 2.

        // make the oldest come first.
        packetBuffer->sort(bf_slot_less());
     
        int waitingForDest = 0;
        List<BufferSlot*>::iterator slotIdx;
        for (slotIdx = packetBuffer->begin();
             slotIdx != packetBuffer->end();
             slotIdx++)
        {
            // pick up the packet in the order of increasing arbitration time
            BufferSlot* slot = *slotIdx;           

            // check the packet is ready to go.
            if (slot->isReady()) {
                if (sendOutPacketFromBuffer(slot) == success) { 
                    bktOutputCyclesFirst->vote(); 
                    busLog->d(" SUCCESS\n");                
                    packetBuffer->erase(slotIdx);
                    delete slot;
                    break;
                } else {
                    bktOutputCyclesDestBusy->vote(); 
                    waitingForDest++;
                    busLog->d(" FAILURE\n");
                }
            }
        }
        histEntitiesWaitingForDest->add(waitingForDest);
    }
}

void Bus::transferArbitratedPacketToBuffer () {
    if (startTransferTime - transferTime < getArbitrationDelay()) {
        bktInputCyclesArbitration->vote();
    } else {
        if (startTransferTime - transferTime - getArbitrationDelay() == 0)
        {
            bktInputCyclesFirst->vote();
        } else {
            bktInputCyclesFollowing->vote();
        }
    }
    transferTime--;

    if (gradualDelivery && (startTransferTime - transferTime == 1)) {
        startSnoop();
    }
    if (transferTime == 0) {
        if (!gradualDelivery) {
            startSnoop();
        }
        
        // go for the next arbitration.
        arbitratedPacket = NULL;
        gradualDelivery = false;
    } 
}

    
void Bus::startSnoop () {   
    // 1. complete the transfer to the buffer
    uint numSnpDevs = sBus->getNumSnoopDevs(this, arbitratedPacket);
    if (numSnpDevs > 0) {
        // we don't want to snoop on the sender.
        numSnpDevs--;
    }

    BufferSlot* freeSlot =
        NEW BufferSlot(arbitratedPacket,
                       global_sim->getSimclock(),
                       numSnpDevs,
                       this,
                       busLog);

    packetBuffer->add(freeSlot);

    // 2. send out snoop request
    if (numSnpDevs > 0 && sBus->isSnooping(this)) {
        broadcastSnoopRequest();
    }
}

bool Bus::isSnooped (Packet* p) {
    return IS_A(DataRequest*, p);
}

void Bus::broadcastSnoopRequest () {     
    if (this == sBus->getRequestBus()) {
        if (isSnooped(arbitratedPacket)) {
            busLog->d("%s:%lli SNOOP: %s\n",
                      name,
                      global_sim->getSimclock(),
                      arbitratedPacket->toString());
            sBus->snoop(this, arbitratedPacket);
         }
    } else {
        // for now, we don't snoop on the response bus.
        ASSERT(false);
    }
}

void Bus::pickUpPacketToArbitrate () {    
    if (packetBuffer->getLength() < bufferSize && packetCount > 0) {
        for (uint i = 0; i < packets.size(); i++) {
            if (arbRobin == packets.end() || addedDevice) {
                arbRobin = packets.begin();
                addedDevice = false;
            }
            std::pair<Entity*, List<Packet*>* > device = *arbRobin;
            List<Packet*>* devQueue = device.second;
            arbRobin++;

            if (devQueue->empty()) {
                continue;
            }

            //
            // found suitable request to go on the bus.
            //
            arbitratedPacket = removePacket(devQueue);

            // network traffic statistics
            sBus->hookRecordTraffic(this, arbitratedPacket);

            busLog->d("%s:%lli   ARB: %s\n",
                      name,
                      global_sim->getSimclock(),
                      arbitratedPacket->toString());

            // we've done arbitration, that took a certain number of
            // cycles...
         
            // calculate time to transfer the arbitrated packet through bus to
            // the available buffer slot.
            calculateTransferTime();

            // at least, it should use the current cycle.
            ASSERT(transferTime >= 1);
            break;
        }
    }
}

bool Bus::isGraduallyDelivered (Packet* p) {
    return false;
}

void Bus::calculateTransferTime() {
    ASSERT(transferTime == 0);

    gradualDelivery = isGraduallyDelivered(arbitratedPacket);

    transferTime =
        getArbitrationDelay() + arbitratedPacket->getSize() /
        getWidth();
    
    // account for the remaining bytes of the packet.
    if (arbitratedPacket->getSize() % getWidth() != 0) {
        transferTime++;
    }
 
    startTransferTime = transferTime;
}

Result Bus::sendOutPacketFromBuffer (BufferSlot* slot) {   
    Packet* pk = slot->getPacket();
    char tmp[128];
    strcpy(tmp, pk->toString());

    if (this == sBus->getRequestBus()) {
        Request* req = CAST_OR_DIE(Request*, pk);

        if (slot->getFlag() == RESPONSE_NO_SNOOP_RESPONSE) {
            // send the DataRequest down to destination
            busLog->d("%s:%lli  DEST: %s\n",
                      name,
                      global_sim->getSimclock(),
                      tmp);
            if (sBus->delegateRoute(this, req) != success) {
                return failure; // dont delete and erase packet below...
            }
        } else if (slot->getFlag() == RESPONSE_SNOOP_RESPONSE) { 
            // there will be a snoopresponse, so dont send it to the
            // lower level.
            busLog->d("%s:%lli  DROP: %s\n",
                      name,
                      global_sim->getSimclock(),
                      tmp);
            // nothing to delete here. packets are deleted in
            // responseBus
        } else if (slot->getFlag() == RESPONSE_NACK_ABORT ||
                   slot->getFlag() == RESPONSE_NACK_BLOCK ||
                   slot->getFlag() == RESPONSE_NACK_RETRY ||
                   slot->getFlag() == RESPONSE_NACK_CONFLICT ||
                   slot->getFlag() == RESPONSE_NACK_SNAPSHOT_DATA)
        { 
            // we have the sender to retry or abort this access.
            busLog->d("%s:%lli  NACK: %s\n",
                      name,
                      global_sim->getSimclock(),
                      tmp);
         
            Response* resp = NEW Response(req, 1);
            resp->setFlag(slot->getFlag());
            
            if (sBus->delegateRoute(this, resp) != success) {
                delete resp;
                return failure; // dont delete and erase packet below...
            }            
        } else {
            ASSERT(false);
        }
    } else if (this == sBus->getResponseBus()) {
        Response* resp = CAST_OR_DIE(Response*, pk);
        ASSERT(slot->getFlag() == RESPONSE_NO_SNOOP_RESPONSE);

        // check if it is SnoopResponse. if it is, redirect it
        // to the original requester of the DataRequest.
        SnoopResponse* snoopResponse = dynamic_cast<SnoopResponse*>(resp);
        if (snoopResponse != NULL) {
            bool dirtyLine = hasDirtyLine(snoopResponse);
            
            resp = getSnoopResult(snoopResponse);
            strcpy(tmp, resp->toString());

            // set the response in slot so that this doesn't happen
            // again in case of deliver failure.
            slot->setPacket(resp);  

            // the snoop response is duplicated and propagated
            // down to the memory hierarchy.
            if(dirtyLine) {
                propagateResponse(resp);
            }
        }  

        // check if the destination is ready to receive.
        busLog->d("%s:%lli  DEST: %s\n",
                  name,
                  global_sim->getSimclock(),
                  tmp);
        if (sBus->delegateRoute(this, resp) != success) {
            ASSERT(false);
        }
    } 

    return success;
}

bool Bus::hasDirtyLine (SnoopResponse* snoopResponse) {
    SnoopRequest* snr = CAST_OR_DIE(SnoopRequest*, snoopResponse->getRequest());
    if(snr->hasDirtyLine()) {
        ASSERT(snoopResponse->getSnoopACK()->getFlag()
                    == RESPONSE_SNOOP_RESPONSE);
    }

    return snr->hasDirtyLine();
}

Response* Bus::getSnoopResult (SnoopResponse* snoopResponse) {
    ResponseFlag respFlag = snoopResponse->getSnoopACK()->getFlag();
    SnoopRequest* snr = CAST_OR_DIE(SnoopRequest*, snoopResponse->getRequest());
    delete snoopResponse;
    DataRequest* dr = CAST_OR_DIE(DataRequest*, snr->getPacket());
    delete snr;
  
    // create a Response for the original sender.
    Response* resp = NULL;
  
    if (Response::isNack(respFlag)) {
        resp = NEW Response(dr,1);    
        // we have the sender to retry or abort this access.
        busLog->d("%s:%lli  RESPONSE WITH NACK: %s\n",
                  name,
                  global_sim->getSimclock(),
                  resp->toString());
  
        resp->setFlag(respFlag);
    } else {
        ASSERT(respFlag == RESPONSE_SNOOP_RESPONSE);

        resp = NEW Response(dr);   
        // we have the sender to retry or abort this access.
        busLog->d("%s:%lli  RESPONSE WITH ACK: %s\n",
                  name,
                  global_sim->getSimclock(),
                  resp->toString());
        resp->setFlag(RESPONSE_DONE);
    }
  
    return resp;
}

void Bus::propagateResponse (Response* resp) {
    LoadRequest* lr = CAST_OR_DIE(LoadRequest*,resp->getRequest());

    // make a new datarequest that is a store...

    // note that the source of this packet is not bus.
    // it is because SplitBut, not Bus, has a routing table.
    StoreRequest* sr = NEW StoreRequest(lr->getAddress(),
                                        lr->getProcessor(),
                                        sBus->getRoutingEntity(),
                                        lr->getDestination(),
                                        lr->getDataSize());
    sr->insertData((void*)lr->getData(), lr->getDataSize());
    sr->initiatingRequest = NULL;
    if (lr->getFlags() != NULL) {
        sr->setFlags(lr->getFlags()->clone());
    }

    if (lr->isFlushed()) {
        sr->setFlushed(true);
    }

    busLog->d("%s:%lli:%0llx:%s PROPAGATE SNOOP RESPONSE for (%s)\n",
              name,
              global_sim->getSimclock(),
              lr->getAddress(),
              sr->toString(),
              lr->getSource()->getName());
    busLog->d("   ");
    for (uint j = 0; j < lr->getDataSize()/WORD_SIZE; j++) {
        busLog->d("%08x ", ((uint*)lr->getData())[j]);
    }
    busLog->d("\n");
    
    sBus->incOutstandingStores();
    
    Result res = sBus->delegateRoute(this, sr);
    ASSERT(res == success);
}


void Bus::addPacket (Packet* a) {
    packets[a->getSource()]->add(a);
    packetCount++;
}

Packet* Bus::removePacket (Entity* src) {
    packetCount--;
    return packets[src]->remove();
}

Packet* Bus::removePacket (List<Packet*>* src) {
    packetCount--;
    return src->remove();
}
#if 0
void Bus::cancelRequests (CPUState* node) {
    int id = node->id;

    // TODO: austenmc: clean this up...
    if (!strcmp(name, "Request")) {
        L2Cache->cancelInvalidRequests(id);
    }

    // check the pipeline/state variables
    //
    // you have to do this (its not just an optimization) b/c higher levels
    // use the same pointers so they might validate a request that's still
    // in the pipe
    //
    List<Request*>::iterator iter;
    for (iter = pipe->begin(); iter != pipe->end(); ) {
        Request* r = *iter;

        if (r->owner->id == id) {
            iter = pipe->erase(iter);
            MemoryRequest* mr = dynamic_cast<MemoryRequest*>(r);
            if (mr != NULL) {
                addrList->remove(ADDR_VOIDSTAR(mr->address));
            }
        } else {
            iter++;
        }
    }

    for (iter = (requests[id])->begin(); iter != (requests[id])->end(); ) {
        Request* r = *iter;

        if (r->owner->id == id) {
            iter = pipe->erase(iter);
            MemoryRequest* mr = dynamic_cast<MemoryRequest*>(r);
            if (mr != NULL) {
                addrList->remove(LESS_ADDR_VOIDSTAR(mr->address));
            }
        } else {
            iter++;
        }
    }
}
#endif

